﻿using System.Linq;
using Nop.Core.Domain.Directory;
using Nop.Core.Plugins;
using Nop.Plugin.Pricing.PreciousMetals.Data;
using Nop.Services.Configuration;
using Nop.Services.Directory;
using Nop.Web.Framework.Web;
using Nop.Services.Localization;
using Nop.Core;
using Nop.Core.Domain.Localization;

namespace Nop.Plugin.Pricing.PreciousMetals
{
    public class PreciousMetalsPricingProvider : BasePlugin, IAdminMenuPlugin
    {
        private readonly PreciousMetalsPricingObjectContext _objectContext;
        private readonly ISettingService _settingService;
        private readonly ILocalizationService _localizationService;
        private readonly IWorkContext _workContext;
        private readonly IMeasureService _measureService;
        private readonly MeasureSettings _measureSettings;

        public PreciousMetalsPricingProvider(PreciousMetalsPricingObjectContext objectContext, ISettingService settingService, ILocalizationService localizationService, IWorkContext workContext, IMeasureService measureService, MeasureSettings measureSettings)
        {
            this._objectContext = objectContext;
            this._settingService = settingService;
            this._localizationService = localizationService;
            this._workContext = workContext;
            this._measureService = measureService;
            this._measureSettings = measureSettings;
        }

        public void BuildMenuItem(Telerik.Web.Mvc.UI.MenuItemBuilder menuItemBuilder)
        {
            var menuText = _localizationService.GetResource("Admin.MenuItems.PreciousMetalsBulkEdit");
            menuItemBuilder.Text(menuText);
            menuItemBuilder.Url("~/Admin/Plugins/PreciousMetals/Admin/List");
        }

        /// <summary>
        /// Install plugin
        /// </summary>
        public override void Install()
        {
            _objectContext.Install();

            var settings = new PreciousMetalsSettings
                               {
                                   LiveQuoteEnabled = true,
                                   CachePeriodInMinutes = 5,
                                   ExcludeFromSubtotalDiscounts = false,
                               };
            _settingService.SaveSetting(settings);

            InstallTroyOunceWeight();
            InstallLocalizationItems();
            base.Install();
        }

        /// <summary>
        /// Uninstall plugin
        /// </summary>
        public override void Uninstall()
        {
            _objectContext.Uninstall();
            base.Uninstall();
        }

        private static bool? _installed;
        internal static bool IsPluginInstalled
        {
            get
            {
                // Check if the plugin is installed.  Cache the result for future checking.
                if (_installed == null)
                    _installed = PluginManager.ReferencedPlugins.Any(p => p.Installed && p.PluginType == typeof(PreciousMetalsPricingProvider));
                return _installed.Value;
            }
        }

        private void InstallTroyOunceWeight()
        {
            // figure out the correct ratio to use based on one of the four preinstalled system weights
            var primaryWeight = this._measureService.GetMeasureWeightById(this._measureSettings.BaseWeightId);
            decimal ratio;
            switch (primaryWeight.SystemKeyword)
            {
                case "grams":
                    ratio = 0.0321507466m;
                    break;
                case "ounce":
                    ratio = 0.911458333m;
                    break;
                case "lb":
                    ratio = 14.5833333m;
                    break;
                case "kg":
                    ratio = 32.1507466m;
                    break;
                default:
                    ratio = 0;
                    break;
            }

            // make sure troyounce weight exists and is correct
            var weight = _measureService.GetMeasureWeightBySystemKeyword("troyounce");
            if (weight != null)
            {
                // if it's not correct, then update it
                if (weight.Ratio != ratio)
                {
                    weight.Ratio = ratio;
                    _measureService.UpdateMeasureWeight(weight);
                }

                // since it exists, we can quit now.
                return;
            }

            // add the new weight
            var order = this._measureService.GetAllMeasureWeights().Max(m => m.DisplayOrder) + 1;
            weight = new MeasureWeight { Name = "troy ounce(s)", SystemKeyword = "troyounce", Ratio = ratio, DisplayOrder = order };
            this._measureService.InsertMeasureWeight(weight);
        }

        private void InstallLocalizationItems()
        {
            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.Enabled", "Enabled");
            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.Enabled.Hint", "Check this box to enable precious metal pricing for this item.");

            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.MetalType", "Metal Type");
            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.MetalType.Hint", "Type of precious metal contained in this item.");

            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.QuoteType", "Quote Type");
            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.QuoteType.Hint", "Type of quote to use when pricing this item.");

            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.Weight", "Weight");
            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.Weight.Hint", "Total weight of the precious metal. Do not include any packaging or other materials.");

            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.WeightUnit", "Weight Unit");
            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.WeightUnit.Hint", "The unit the weight of this item is specified in.");

            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.TierPriceType", "Tier Pricing Type");
            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.TierPriceType.Hint", "Type of tier pricing");

            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.PercentMarkup", "Percent Markup");
            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.PercentMarkup.Hint", "Increases the total calculated price by a percentage.");

            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.FlatMarkup", "Flat Markup");
            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.FlatMarkup.Hint", "Increases the total calculated price by a flat amount.");

            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.MathType", "Markup Forumula");
            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.MathType.Hint", "Determines which order to apply the two markup fields when both are used.");

            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.LowerAmount", "Minimum Price");
            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.LowerAmount.Hint", "If populated, the price will never fall below this value.");

            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.CurrentPrice", "Current Price");
            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.CurrentPrice.Hint", "The current calculated price of this item.");

            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.ProductCategory", "Product Category");
            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.ProductCategory.Hint", "Select the product category that you want to apply these changes to.");

            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.ProductVariantTitle", "Product");

            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.PriceRoundingType", "Type of price rounding");
            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.PriceRounding", "Price rounding number");

            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.BulkEdit", "Switch to Bulk Edit");
            this.AddOrUpdateLocalizedText("Admin.Catalog.PreciousMetal.ListEdit", "Switch to List Edit");

            this.AddOrUpdateLocalizedText("Admin.Catalog.Products.PreciousMetalPricing.ListEdit", "Precious Metals List Edit");
            this.AddOrUpdateLocalizedText("Admin.Catalog.Products.PreciousMetalPricing.BulkEdit", "Precious Metals Bulk Edit");

            this.AddOrUpdateLocalizedText("Admin.Catalog.Product.SelectOverrideToEdit", "First check the box next to each item you want to update.  Then select the value that will override the existing one.");
            
            this.AddOrUpdateLocalizedText("Admin.Catalog.Products.Variants.PreciousMetals", "Precious Metals Pricing");

            this.AddOrUpdateLocalizedText("Admin.MenuItems.PreciousMetalsBulkEdit", "Precious Metals Bulk Edit");
        }

        private void AddOrUpdateLocalizedText(string resourceName, string resourceValue)
        {
            var language = _workContext.WorkingLanguage;
            var resource = _localizationService.GetLocaleStringResourceByName(resourceName, language.Id, false);
            if (resource != null)
            {
                if (resource.ResourceValue != resourceValue && language.Name == "English")
                {
                    // since we are using english and we have a different name, then these have changed.
                    // drop them so we can recreate below
                    _localizationService.DeleteLocaleStringResource(resource);
                }
                else
                {
                    // the resource exists and either it's the same, or we're not using english.
                    return;
                }
            }

            // create the new resource and add it
            resource = new LocaleStringResource
                               {
                                   Language = language,
                                   ResourceName = resourceName,
                                   ResourceValue = resourceValue
                               };
            _localizationService.InsertLocaleStringResource(resource);
        }
    }
}